use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::{Arc, Mutex, MutexGuard, TryLockResult};
use std::task::{Context, Poll};
use std::task::Poll::{Pending, Ready};
use std::time::Duration;
use serde_json::json;
use crate::logger::logger::Logger;
use crate::protocol_buffer::exts::{ToJson, FromJson};

use super::message_pipe::{MessagePipe, StoringImportance};
use super::storage::StorageSystem;


// /**
//  * @author Almas software technologies muhtarjaan mahmood (مۇختەرجان مەخمۇت)
//  * @email ug-project@outlook.com
//  * @create date 2022-07-16 15:37:36
//  * @modify date 2022-07-16 15:37:36
//  * @desc [description]
//  */
//
// enum State {
//     NotStartedYet,
//     Pending,
//     GotTheLock,
// }
// pub struct AsyncLockResult<'a, T> {
//     mutex: Arc<&'a Mutex<T>>,
//     state: State,
// }
//
// impl<'a, T: 'a> Future for AsyncLockResult<'a, T> {
//     type Output = MutexGuard<'a ,T>;
//     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
//         match self.state {
//             NotStartedYet => {
//                 let mutex = self.mutex.clone();
//                 // match mutex.try_lock() {
//                     // Ok(mutex_guard) => {
//                     //     self.state = State::GotTheLock;
//                     //     Ready(mutex_guard)
//                     // }
//                     // Err(error) => {
//                     //     tokio::time::sleep(Duration::from_millis(1)).poll(self, cx);
//                     // }
//                 // }
//                 Pending
//             }
//             State::Pending => Pending,
//             State::GotTheLock => {
//                 Poll::Ready(self.guard.unwrap())
//             }
//         }
//     }
// }
// // #[async_trait::async_trait]
// pub trait MyAsyncMutex<'a, T> {
//     fn lock_and_wait_if_already_locking(&'a self) -> AsyncLockResult<'a, T>;
// }
//
// impl<'a, T> MyAsyncMutex<'a, T> for Mutex<T> {
//     fn lock_and_wait_if_already_locking(&'a self) -> AsyncLockResult<'a, T> {
//         AsyncLockResult {
//             mutex: Arc::new(self),
//             state: State::NotStartedYet,
//             guard: None,
//         }
//     }
// }


// #[async_trait::async_trait]
// pub trait MyAsyncMutex<'a, T: Send> {
//     async fn lock_and_wait_if_already_locking(&self) -> MutexGuard<'a, T>;
// }
//
// #[async_trait::async_trait]
// impl<'a, T: Send> MyAsyncMutex<'a, T> for Mutex<T> {
//     async fn lock_and_wait_if_already_locking(&self) -> MutexGuard<'a, T> {
//         let mut guard;
//         loop {
//             match self.try_lock() {
//                 Ok(_guard) => {
//                     guard = _guard;
//                     break
//                 }
//                 Err(error) => {
//                     tokio::time::sleep(Duration::from_millis(1)).await;
//                 }
//             }
//         }
//         guard
//     }
// }

pub trait Apply {
    fn apply<F: FnMut(&mut Self) -> ()>(&mut self, f: F) -> &mut Self;
}

impl<T> Apply for T {
    fn apply<F: FnMut(&mut Self) -> ()>(&mut self, mut f: F) -> &mut Self {
        f(self);
        self
    }
}

impl<LOGGER: Logger + Send + Sync> ToJson for MessagePipe<LOGGER> {
    fn to_json(&self) -> serde_json::Value {
        json!({
            "uuid": self.uuid,
            "name": self.name,
            "description": self.description,
            "hasTable": self.has_table,
            "importance": match self.message_importance {
                StoringImportance::Usual => 0,
                _ => 1,
            },
        })
    }
}

impl<LOGGER: Logger + Send + Sync + 'static> MessagePipe<LOGGER> {
    pub fn from_json(storage: Arc<dyn StorageSystem + Send + Sync>, value: &serde_json::Value, logger: Arc<LOGGER>) -> MessagePipe<LOGGER> {
        fn get_string_from_value(value: &serde_json::Value) -> String {
            value.as_str().unwrap().into()
        }
        MessagePipe::new(get_string_from_value(&value["uuid"]), get_string_from_value(&value["name"]), get_string_from_value(&value["description"]), match value["importance"].as_i64().unwrap() {
            0 => StoringImportance::Usual,
            _ => StoringImportance::Critical,
        }, value["hasTable"].as_bool().unwrap(), storage, logger)
    }
}